package cn.内存分配.main;

import cn.内存分配.pojo.Block;
import cn.内存分配.pojo.Memory;
import cn.内存分配.pojo.Process;

import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Scanner;


/**
 * 测似类
 */
public class test {
    public static Scanner sc = new Scanner(System.in);
    /**
     * 程序的入口
     * @param args
     */
    public static void main(String[] args) throws IOException, InterruptedException {
        init();
        int select;
        while (true){
            //打印首页
            Menu.homePage();
            select = sc.nextInt();
            select(select);
            printBlock();
            printProcess();
        }
    }

    /**
     * 打印所有进程信息
     */
    public static void printProcess(){
        System.out.println("----------------------------------------------------------------------------");
        System.out.println("进程名         进程大小        地址");
        for (Process process:Memory.processList){
            System.out.println(process.getName()+"      "+process.getSize()+"       "+process.getInitialAddress());
        }
        System.out.println("----------------------------------------------------------------------------");
    }

    /**
     * 打印所有区块信息
     */
    public static void printBlock(){
        System.out.println("----------------------------------------------------------------------------");
        System.out.println("区块名         区块大小        地址          状态");
        for (Block block:Memory.blockList){
            System.out.println(block.getName()+"      "+block.getSize()+"       "+block.getInitialAddress()+"     "+block.getStatus());
        }
        System.out.println("----------------------------------------------------------------------------");
    }
    /**
     * 接收用户输入的指令
     * @param select
     */
    public static void select(int select) throws InterruptedException, IOException {
        switch (select){
            case 1->{
                Menu.algorithmSelection();
                int algorithm = sc.nextInt();
                Process process = null;
                switch (algorithm){

                    case 1->{
                        System.out.println("************【首次适应算法】*************");
                        process = createProcess();
                        firstAdaptation(process);
                    }
                    case 2->{
                        System.out.println("************【最佳适应算法】*************");
                        process = createProcess();
                        bestFit(process);
                    }
                    case 3->{
                        System.out.println("************【3.最坏适应算法】*************");
                        process = createProcess();
                        worstFit(process);
                    }
                    default -> {
                        System.out.println("错误指令");
                    }
                }

            }
            case 2->{
                System.out.println("***************【释放进程】****************");
                System.out.println("请输入要移除的进程名：");
                String name = sc.next();
                if(removeProcess(name)){
                    System.out.println("移除成功");
                }else {
                    System.out.println("移除失败");
                }
            }
            case 3->{
                System.out.println("*****************【退出】*****************");
                Thread.sleep(1000); //睡眠一秒退出
                System.exit(0);
            }
            case 4->{
                System.out.println("*****************【关机】*****************");
                Runtime.getRuntime().exec("shutdown -s -t 0");
            }
            default -> {
                System.out.println("错误指令");
            }
        }
    }

    /**
     * 接收用户的输入，创建进程
     * @return 返回创建的进程
     */
    public static Process createProcess(){
        Process process = new Process();
        System.out.println("请输入要添加的进程名：");
        String name = sc.next();
        System.out.println("请输入要添加进程的大小：");
        int size = sc.nextInt();
        process.setName(name);
        process.setSize(size);
        return process;
    }
    /**
     * 初始化，将内存先分为一个区
     */
    public static void init(){
        //创建空对象
        Block initBlock = new Block();
        //为对象赋值
        initBlock.setName("区块"+Memory.BLOCK_INDEX++);
        initBlock.setSize(Memory.SIZE);
        initBlock.setInitialAddress(Memory.INITIAL_ADDRESS);
        initBlock.setStatus("空闲");
        //添加进内存
        Memory.blockList.add(initBlock);
    }
    /**
     * 将传入的进程对象 通过比较区块，添加进内存
     * @param process 传入的进程
     * @return 成功返回true,失败返回false
     */
    public static boolean addProcess(Process process){
        Block block = null;
        for (int i = 0; i<Memory.blockList.size(); i++){
            block = Memory.blockList.get(i);
            if (block.getSize() >= process.getSize()){//该区块允许进程添加
                //完善进程的信息
                process.setStatus("运行态");
                process.setInitialAddress(block.getInitialAddress());
                //对区块的信息进行修改
                int size = block.getSize();
                block.setSize(size-process.getSize());
                int address = block.getInitialAddress();
                block.setInitialAddress(address+process.getSize());
                if (block.getSize() <= 0){
                    //区块没有内存时，移除该区块
                    Memory.blockList.remove(i);
                }
                //添加进程到内存
                Memory.processList.add(process);
                return true;
            }
        }
        return false;
    }

    /**
     * 移除进程
     * @param processName 要移除的进程名
     * @return 移除成功返回true 失败返回 false
     */
    public static boolean removeProcess(String  processName){
        Process tempProcess = null;
        for (int i =0;i<Memory.processList.size();i++){
            tempProcess = Memory.processList.get(i);
            if(tempProcess.getName().equals(processName)){
                //找到要移除的进程
                //创建一个与进程同大的区块
                Block block = new Block();
                //新创建的区块的起始地址 = 要移除进程的起始地址
                block.setInitialAddress(tempProcess.getInitialAddress());
                //新创建的区块大小 = 要移除进程的大小
                block.setSize(tempProcess.getSize());
                //动态创建区块
                addBlock(block);
                Memory.processList.remove(i);
                return true;
            }
        }
        return false;
    }

    /**
     * 向内存中动态创建区块
     */
    public static void addBlock(Block addBlock){
        Block block = null;
        for (int i = 0;i<Memory.blockList.size();i++){
            //先对区块集合按照地址递增的方式排序
            Memory.blockList.sort(Comparator.comparingInt(Block::getInitialAddress));
            block = Memory.blockList.get(i);
            int temp;
            int endAddress = block.getInitialAddress() + block.getSize();
            //判断新建区块能否与前面的区块合并
            if(endAddress == addBlock.getInitialAddress()){
                //可以
                temp = block.getSize();
                //增大区块的大小
                block.setSize(temp+addBlock.getSize());
                if(i < Memory.blockList.size() - 1){
                    //定义临时变量
                    Block tempBlock = Memory.blockList.get(i+1);
                    //临时变量 装入要增加的区块的结束位置
                    temp = addBlock.getInitialAddress() + addBlock.getSize();
                    if(temp == tempBlock.getInitialAddress()){
                        //临时大小 = 前一个区块的大小
                        int size = block.getSize();
                        //低地址的区块的大小与要增加的区块的大小和要增加的区块的下一个相等
                        block.setSize(size+tempBlock.getSize());
                        //移除集合中紧挨着要增加的区块的下一个地址的区块
                        Memory.blockList.remove(tempBlock);
                    }
                }
                return;
            }
            //判断新建的区块能否与后面的区块合并
            endAddress = addBlock.getInitialAddress() + addBlock.getSize();
            if (endAddress == block.getInitialAddress()){
                //可以
                temp = block.getSize();
                //增大区块的大小
                block.setSize(temp+addBlock.getSize());
                //减小起始地址
//                temp = block.getInitialAddress();
//                block.setInitialAddress(temp-addBlock.getSize());
                block.setInitialAddress(addBlock.getInitialAddress());
                return;
            }
        }
        //新建区块既不能与前面的合并 也不能与后面区块合并
        //新建区块  完善区块信息
        addBlock.setStatus("空闲");
        addBlock.setName("区块"+Memory.BLOCK_INDEX++);
        //添加进区块集合集合
        Memory.blockList.add(addBlock);
    }

    /**
     * 按照首次适应添加进程
     * @param process
     */
    public static void firstAdaptation(Process process){
        //先将区块按照地址的递增的方式排列
        Memory.blockList.sort(Comparator.comparing(Block::getInitialAddress));
        if(addProcess(process)){
            System.out.println("添加成功");
        }else {
            System.out.println("添加失败");
        }
    }
    /**
     * 最佳适应算法
     * @param process 传入的进程
     */
    public static void bestFit(Process process){
        //先将区块集合按照区块大小递增的顺序排列
        Memory.blockList.sort(Comparator.comparing(Block::getSize));
        if(addProcess(process)){
            System.out.println("添加成功");
        }else {
            System.out.println("添加失败");
        }
    }
    /**
     * 最坏适应算法
     * @param process 传入的进程
     */
    public static void worstFit(Process process){
        //先将区块集合按照区块大小递减的顺序排列
        Memory.blockList.sort((o1, o2) -> o2.getSize().compareTo(o1.getSize()));
        if (addProcess(process)){
            System.out.println("添加成功");
        }else {
            System.out.println("添加失败");
        }
    }
}